
#include <iostream>
#include <cerrno>
#include <unistd.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <wait.h>
#include <sys/stat.h>
#include "Task.hpp"
using namespace std;

// master
class Channel // 维护管道信息
{
public:
    Channel(int wfd, int subprocessid, string name)
        : _wfd(wfd), _subprocessid(subprocessid), _name(name)
    {
    }

    int GetWfd() { return _wfd; }
    int GetProcessId() { return _subprocessid; }
    string GetName() { return _name; }

    void CloseChannel()
    { // 关闭信道的写端
        close(_wfd);
    }

    void Wait()
    { // 关闭工作进程
        int status = 0;
        pid_t rid = waitpid(_subprocessid, NULL, 0);
        if (rid > 0)
        {
            cout << "wait " << rid << " success" << endl;
        }
    }

    ~Channel()
    {
    }

private:
    int _wfd;          // 信道以写方式打开的文件描述符
    int _subprocessid; // 读端进程的pid
    string _name;      // 信道的命名
};

// 创建信道和子进程
void CreatChannelAndSub(int num, vector<Channel> &Channels, task_t task)
{ // task是一个回调函数
    for (int i = 0; i < num; i++)
    {
        // 1.创建管道
        int pipefd[2];
        int n = pipe(pipefd);
        if (n < 0)
            exit(0);

        // 创建子进程
        pid_t pid = fork();
        if (pid == 0)
        {
            // 关闭写端
            close(pipefd[1]);
            dup2(pipefd[0], 0); // 将管道的读端重定向到标准输入
            task();             // 子进程执行的任务
            close(pipefd[0]);
            exit(0);
        }
        // 父进程关闭读端，只用来输出任务
        close(pipefd[0]);
        string name = "Channel-" + to_string(i);
        // 将创建的信道存入输出型参数中
        Channels.push_back({pipefd[1], pid, name});
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next = (next + 1) % channelnum;
    return channel;
}

void SendTask(int taskcommand, Channel &channel)
{
    write(channel.GetWfd(), &taskcommand, sizeof(taskcommand));
}

// 具体任务
void CtrlProcessOnce(vector<Channel> &Channels)
{
    // 1.选择一个任务
    int taskcommand = SelectTask();
    // 2.选择一个空信道
    int taskchannel = NextChannel(Channels.size());

    // 3.发送任务编号给信道
    SendTask(taskcommand, Channels[taskchannel]);

    cout << "taskcommand: " << taskcommand << " channel: " << Channels[taskchannel].GetName() << " sub process: " << Channels[taskchannel].GetProcessId() << endl;
}

// 控制子进程执行任务
void CtrlProcess(vector<Channel> &Channels, int time = -1)
{ // time表示分配多少次任务给子进程去执行，默认是无限
    if (time == -1)
    { // 默认
        while (true)
        {
            CtrlProcessOnce(Channels);
            sleep(1);
        }
    }
    else if (time > 0)
    {
        while (time--)
        {
            CtrlProcessOnce(Channels);
            sleep(1);
        }
    }
}

void CleanUpChannel(vector<Channel> &channels)
{
    for (auto &it : channels)
    {
        it.CloseChannel();
    }

    for (auto &it : channels)
    {
        it.Wait();
    }
}

int main(int argc, char *argv[])
{ // 参数列表，argv[1]表示创建的工作进程的数量，即内存池大小
    if (argc != 2)
    {
        cerr << "Usage: " << argv[0] << " Processnum" << endl;
        return 1;
    }
    int num = stoi(argv[1]);
    // 加载任务
    LoadTask();
    vector<Channel> Channels; // 进程池

    // 创建信道和子进程
    CreatChannelAndSub(num, Channels, work1);

    // 通过Channel控制子进程
    CtrlProcess(Channels, 10);
    // 关闭子进程和信道的写端
    CleanUpChannel(Channels);
    return 0;
}